package com.gframework.mybatis.util.generator.core.codegen;

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.gframework.mybatis.util.generator.core.api.GeneratorConst;
import com.gframework.mybatis.util.generator.core.codegen.dom.java.Interface;
import com.gframework.mybatis.util.generator.core.codegen.dom.java.JavaVisibility;
import com.gframework.mybatis.util.generator.core.codegen.dom.java.Method;
import com.gframework.mybatis.util.generator.core.codegen.dom.xml.Attribute;
import com.gframework.mybatis.util.generator.core.codegen.dom.xml.Document;
import com.gframework.mybatis.util.generator.core.codegen.dom.xml.XmlElement;
import com.gframework.mybatis.util.generator.core.conf.Configuration;
import com.gframework.mybatis.util.generator.core.conf.MetaData;
import com.gframework.mybatis.util.generator.core.interceptor.JavaBeanInterceptor;
import com.gframework.mybatis.util.generator.core.interceptor.JavaDaoInterceptor;
import com.gframework.mybatis.util.generator.core.interceptor.XmlElementInterceptor;
import com.gframework.mybatis.util.generator.core.util.ClassScanner;

/**
 * 本类仅仅作为一个内部伪工厂类的功能，用于取得所有sqlOperator接口的子类，并生成dao和xml代码.
 * <p>
 * 如果你想要设置bean创建拦截器，请参考{@link JavaBeanInterceptor}。
 * </p>
 * <p>
 * 如果你想自定义sql方法，请参考{@link SqlOperatorGenerator}。
 * </p>
 * <p>
 * 如果你想要设置dao接口创建拦截器，请参考{@link JavaDaoInterceptor}。
 * </p>
 * 
 * @since 1.0.0
 * @author Ghwolf
 */
public class DaoXmlGenerator {

	/**
	 * 全局配置对象
	 */
	private Configuration config;
	/** xml publicId. */
	private static final String PUBLIC_ID = "-//mybatis.org//DTD Mapper 3.0//EN";
	/** xml systemId. */
	private static final String SYSTEM_ID = "http://mybatis.org/dtd/mybatis-3-mapper.dtd";

	/** xml中element顺序，出现指定名称之外的节点，都会放到后面. */
	private Map<String,Integer> xmlOrder = new HashMap<>();
	
	{
		xmlOrder.put("sql",0);
		xmlOrder.put("resultMap",1);
		xmlOrder.put("insert",2);
		xmlOrder.put("update",3);
		xmlOrder.put("delete",4);
		xmlOrder.put("select",5);
		xmlOrder.put("cache",6);
	}
	
	public DaoXmlGenerator(Configuration config) {
		this.config = config;
	}

	/**
	 * 创建一个mybatis的xml配置文件.
	 * 如果返回null表示不生成
	 * 
	 * @param table 当前操作表
	 * @return 返回Document对象
	 */
	public Document createXml(MetaData table) {
		XmlElementInterceptor inter = this.config.getXmlElementInterceptor();
		Document document = getBasicDocument(table);
		XmlElement mapper = document.getRootElement();
		XmlElementInterceptor xmlInter = this.config.getXmlElementInterceptor();
		if (xmlInter != null) {
			mapper = xmlInter.createRootElement(table, mapper, this.config);
		}
		if (mapper == null) {
			return null ;
		}

		List<Class<? extends SqlOperatorGenerator>> list = this.getAllSqlGenerators();

		@SuppressWarnings("unchecked")
		List<XmlElement>[] els = new List[xmlOrder.size() + 1];
		for (int x = 0 ; x < els.length ; x ++) {
			els[x] = new ArrayList<>();
		}
		
		for (Class<?> cls : list) {
			try {
				boolean b1 = this.config.isNeedCanUpdateInterfaceInvalid();
				boolean b2 = NeedCanUpdate.class.isAssignableFrom(cls);
				boolean b3 = table.isReadonly();
				if (!b1 && b2 && b3) {
					continue ;
				}
				boolean b4 = NeedPrimaryKey.class.isAssignableFrom(cls);
				boolean b5 = table.getPrimaryKey() == null ;
				if (b4 && b5) {
					continue ;
				}
				SqlOperatorGenerator g = (SqlOperatorGenerator) cls.newInstance();
				XmlElement m = g.createSqlXml(table, this.config);
				if (inter != null) {
					m = inter.createXmlElement(table, m, this.config);
				}
				if (m != null) {
					Integer order = this.xmlOrder.get(m.getName());
					if (order == null) {
						order = els.length - 1;
					}
					els[order].add(m) ;
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		
		for (List<XmlElement> xmlList : els) {
			for (XmlElement xmlEle : xmlList){
				mapper.addElement(xmlEle);
			}
		}
		
		return document;
	}

	/**
	 * 取得一个基本的xml document结构对象.
	 * 调用本方法后，root节点、publicId、systemId都会被创建
	 * 
	 * @param table 当前操作表
	 * @return 返回Document类对象
	 */
	private Document getBasicDocument(MetaData table) {
		Document doc = new Document(PUBLIC_ID, SYSTEM_ID);
		doc.setFileName("I" + table.getFormatTableName() + "DAOMapper.xml");
		XmlElement mapper = new XmlElement("mapper");
		Attribute namespace = new Attribute("namespace",
				this.config.getBasePackage() + "." + table.getModuleName() + ".dao.I" + table.getFormatTableName() + "DAO");
		mapper.addAttribute(namespace);
		mapper.addComment(GeneratorConst.VERSION_REMARK);
		mapper.addComment("如果你需要自定义resultMap，sql，包括缓存等，请在同目录下编写一个namespace相同的xml去编写，namespace相同的xml内容是共享的");
		mapper.addComment("操作表：" + table.getTableName());
		mapper.addComment("表注释：" + table.getTableComment());
		doc.setRootElement(mapper);
		return doc;
	}

	/**
	 * 创建一个mybatis的dao接口类.
	 * 如果返回null表示不生成
	 * 
	 * @param table 当前操作表
	 * @return 返回Interface对象
	 */
	public Interface createDao(MetaData table) {
		JavaDaoInterceptor jdinter = this.config.getJavaDaoInterceptor();
		Interface inter = this.getBasicInterface(table, jdinter);
		if (inter == null) {
			return null;
		}

		List<Class<? extends SqlOperatorGenerator>> list = this.getAllSqlGenerators();

		for (Class<?> cls : list) {
			try {
				boolean b1 = this.config.isNeedCanUpdateInterfaceInvalid();
				boolean b2 = NeedCanUpdate.class.isAssignableFrom(cls);
				boolean b3 = table.isReadonly();
				if (!b1 && b2 && b3) {
					continue ;
				}
				boolean b4 = NeedPrimaryKey.class.isAssignableFrom(cls);
				boolean b5 = table.getPrimaryKey() == null ;
				if (b4 && b5) {
					continue ;
				}
				SqlOperatorGenerator g = (SqlOperatorGenerator) cls.newInstance();
				Method m = g.createMethod(table, this.config);
				if (jdinter != null) {
					m = jdinter.createMethod(table, inter, m, this.config);
				}
				if (m != null) {
					inter.addMethod(m);
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

		return inter;
	}

	/**
	 * 取得接口结构对象
	 * 
	 * @param table 当前操作表格
	 * @param jdinter 拦截器对象，可以为null
	 * @return 返回Interface类对象
	 */
	private Interface getBasicInterface(MetaData table, JavaDaoInterceptor jdinter) {
		String fullInterName = this.config.getBasePackage() + "." + table.getModuleName() + ".dao.I" + table.getFormatTableName() + "DAO";
		Interface inter = new Interface(fullInterName);
		inter.setVisibility(JavaVisibility.PUBLIC);

		inter.addJavaDocLine("/** ");
		inter.addJavaDocLine(" * " + table.getTableName() + "表对应的DAO操作接口.<br>");
		inter.addJavaDocLine(" * " + table.getTableComment());
		inter.addJavaDocLine(" * ");
		inter.addJavaDocLine(" * <p>" + GeneratorConst.VERSION_REMARK + "</p>");
		inter.addJavaDocLine(" * @since " + LocalDate.now().toString());
		inter.addJavaDocLine(" * @author " + GeneratorConst.AUTHOR);
		inter.addJavaDocLine(" * @version " + GeneratorConst.VERSION);
		inter.addJavaDocLine(" */");

		if (jdinter != null) {
			return jdinter.createInterface(table, inter, this.config);
		} else {
			return inter;
		}
	}

	/**
	 * 取得所有的sql操作接口的子类.
	 * 
	 * @return 如果没有集合长度为0
	 */
	private List<Class<? extends SqlOperatorGenerator>> getAllSqlGenerators() {
		List<Class<? extends SqlOperatorGenerator>> list = new ArrayList<>();
		list.addAll(this.config.getCustomSqlGenerators());
		for (String pac : this.config.getCustomSqlGeneratorPackage()) {
			List<Class<SqlOperatorGenerator>> li = ClassScanner.scannerPackage(pac, SqlOperatorGenerator.class);
			list.addAll(li);
		}
		List<Class<SqlOperatorGenerator>> defaultList = ClassScanner.scannerPackage("com.gframework.mybatis.util.generator.core.codegen.gen",
				SqlOperatorGenerator.class);
		list.addAll(defaultList);

		return list;
	}
	public static void main(String[] args) {
		System.out.println(SqlOperatorGenerator.class.getPackage().getName());
	}

}
